home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume89
/
librarys
/
minrex04.1
< prev
next >
Wrap
Text File
|
1989-05-11
|
77KB
|
2,016 lines
Path: xanth!ames!sun-barr!sun!swap!page
From: page%swap@Sun.COM (Bob Page)
Newsgroups: comp.sources.amiga
Subject: v89i126: minrexx - add a rexx port to your programs v0.4
Message-ID: <104262@sun.Eng.Sun.COM>
Date: 11 May 89 07:28:59 GMT
Sender: news@sun.Eng.Sun.COM
Lines: 2005
Approved: page@sun.com
Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
Posting-number: Volume 89, Issue 126
Archive-name: libraries/minrexx04.1
This is the latest version of Tom Rokicki's code to show people how to
add a 7-function ARexx port to their own code.
[uuencoded executable included. ..bob]
# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
# README
# freedraw.c
# minrexx.c
# minrexx.h
# Makefile
# freeedraw.uu
# aspline.fd
# bspline.fd
# sample.fd
# saspline.fd
# sbspline.fd
# version.fd
# This is archive 1 of a 1-part kit.
# This archive created: Thu May 11 00:22:53 1989
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
XHere's "minrexx", a simple ARexx interface which can be easily patched into
Xalmost any program, and an example from Fish Disk 1, where I add an ARexx
Xport to freedraw.
X
XFor documentation on Minrexx, refer to the .c and .h file.
X
XFor documentation on the Freedraw port, see the portions of freedraw.c
Xdelimited by "#ifdef TALKTOREXX".
X
XEnjoy!
SHAR_EOF
echo "extracting freedraw.c"
sed 's/^X//' << \SHAR_EOF > freedraw.c
X#ifdef TALKTOREXX
X/*
X * This program is an example of how to add a Rexx port to a given
X * program. I thought it particularly appropriate to grab a program
X * off Fred Fish Disk 1. All the REXX stuff is bracketed by `ifdef
X * TALKTOREXX', so you can identify it easily. If you compile with
X * TALKTOREXX unset, you will get the default program with no REXX
X * port.
X *
X * The REXX port on this program adds another 3K to the executable
X * size, but a lot of functionality comes with that. You can draw
X * from Rexx, spawn macros from Rexx, etc. But go to the next
X * TALKTOREXX for more information.
X *
X * To run a rexx macro on startup, simply give that rexx macro as
X * part of the command line, as in
X *
X * freedraw sample
X *
X * or
X *
X * freedraw bspline 20 100 20 20 280 20 280 100
X *
X * All modifications are by Radical Eye Software, and all modifications
X * are placed in the public domain.
X */
X#endif
X/************************************************************************/
X/*** FreeDraw - PD graphics for Amiga ***/
X/*** ***/
X/*** This is an extremely simple graphics editor which works in ***/
X/*** the windowing environment of the Amiga. It is very limited ***/
X/*** in features, but I hope to add a lot more, and I would be ***/
X/*** happy to receive assistance from anyone who wants to give it. ***/
X/*** The basic idea of this program is to provide some minimal ***/
X/*** image editing functions which can be used to develop images ***/
X/*** for other programs. I know there will be a lot of Paint type ***/
X/*** type programs avaialable soon, but what are we supposed to use ***/
X/*** now? The most important features to add now will probably be ***/
X/*** those related to "cut and paste", disk srtorage and retrieval, ***/
X/*** and "single-pixel" editing like Mac's "fatbits". ***/
X/*** I intend to use the IFF standard for the image storage and ***/
X/*** retrieval and will be coding a "Files" menu soon. The work ***/
X/*** required for "cut and paste" should be almost trivial, but I ***/
X/*** still may not get to it for a while. Fatbits editing from one ***/
X/*** window to another involves some manipulations of the RastPorts ***/
X/*** which still elude me, as I have only recently begun to use the ***/
X/*** Amiga and don't yet understand some important details of these ***/
X/*** structures. This would be a great item for one of the genius ***/
X/*** members of the Amiga programming community to provide. ***/
X/*** There are only two menu topics in this version, so using it ***/
X/*** is really quite easy. Boxes are not allowed to be drawn in ***/
X/*** areas outside of the window where border gadgets are located, ***/
X/*** and the pen-draw mode also clips to these same boundaries. If ***/
X/*** you have begun to draw a box by clicking the left button while ***/
X/*** the cursor is located in the FreeDraw window, then you can ***/
X/*** cancel that box by clicking the right button. In the pen mode ***/
X/*** pressing and holding the left button will draw. Colors are ***/
X/*** selected by simply releasing the menu button over the desired ***/
X/*** color in the Color menu. The erase feature always clears the ***/
X/*** window to the currently selected color. ***/
X/*** This is no gem of programming style, but you're getting it ***/
X/*** for the right price so be patient with its design flaws. New ***/
X/*** versions will appear here on BIX as soon as I can get them in ***/
X/*** shape for release. I apologize to anyone who objects to my ***/
X/*** lack of coding grace, but I just want to get the project off ***/
X/*** the ground, and improvements will be forthcoming. There are ***/
X/*** a lot of comments, but I didn't know what needed to be made ***/
X/*** clear so I just commented everything. ***/
X/*** ***/
X/*** If you like the idea of a PD graphics program and would be ***/
X/*** interested in doing some development work, then please write ***/
X/*** me at the address listed below, or call if you prefer. I do ***/
X/*** want to know if there is any interest in such a project, so ***/
X/*** I will be glad to discuss any ideas you might have. Also, as ***/
X/*** I do not currently use CompuServe or any other major nets, I ***/
X/*** would appreciate if someone would post this listing there. ***/
X/*** I hope somebody enjoys this. Have Fun. ***/
X/*** Rick Ross 11/14/85 ***/
X/*** ***/
X/*** My address: ***/
X/*** Richard M. Ross, Jr. ***/
X/*** Eidetic Imaging ***/
X/*** 740 N. 22nd Street ***/
X/*** Philadelphia, PA 19130 ***/
X/*** ***/
X/*** Phone - (215) 236-7388 ***/
X/************************************************************************/
Xchar *VERSION = "Freedraw 0.01 by Richard M. Ross" ;
X/* compiler directives to fetch the necessary header files */
X
X#include <exec/types.h>
X#include <exec/exec.h>
X#include <intuition/intuition.h>
X#include <intuition/intuitionbase.h>
X#include <graphics/gfx.h>
X#include <graphics/regions.h>
X#include <graphics/copper.h>
X#include <graphics/gels.h>
X#include <graphics/gfxbase.h>
X#include <devices/keymap.h>
X#include <hardware/blit.h>
X
X/* These definitions are used by intuition for
X * calls to OpenLibrary() in order to ensure
X * that an appropriate ROM revision is
X * available.
X */
X
X#define INTUITION_REV 1L
X#define GRAPHICS_REV 1L
X
X/* Intuition always wants to see these declarations */
Xstruct IntuitionBase *IntuitionBase;
Xstruct GfxBase *GfxBase;
X
X/* This is the Window structure declaration.
X * Nothing fancy is going on here, but note
X * the Flags and IDCMPFlags members of the
X * structure define which messages will be
X * sent by Intuition. I haven't used them all
X * and if you want to change the settings you
X * should probably do it her instead of using
X * ModifyIDCMP later.
X */
X
Xstruct NewWindow NewWindow = {
X 10,
X 10,
X 600,
X 180,
X 0,
X 1,
X CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS | MENUPICK
X | NEWSIZE | INACTIVEWINDOW,
X WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG
X | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE,
X NULL,
X NULL,
X (UBYTE *)"AMIGA FreeDraw 0.01",
X NULL,
X NULL,
X 100, 35,
X -1, -1,
X WBENCHSCREEN,
X};
X#ifdef TALKTOREXX
X/*
X * We need our include file.
X */
X#include "minrexx.h"
X/*
X * These are the REXX functions defined at the bottom of the file.
X */
Xvoid rexxcolor(), rexxbox(), rexxfbox(), rexxline(), rexxtofront(),
X rexxtoback(), rexxexit(), rexxversion(), rexxspawn() ;
Xint disp() ;
X/*
X * Here is our command association list. Note that in this case,
X * we are setting the userdata field to be a function to call.
X * Dispatch will still take place through disp(), so common head
X * and tail stuff can go there.
X *
X * Commands are all lower case, so we match either upper or lower.
X * (This is a requirement of minrexx.)
X */
Xstruct rexxCommandList rcl[] = {
X { "color", (APTR)&rexxcolor },
X { "box", (APTR)&rexxbox },
X { "fbox", (APTR)&rexxfbox },
X { "line", (APTR)&rexxline },
X { "tofront", (APTR)&rexxtofront },
X { "toback", (APTR)&rexxtoback },
X { "exit", (APTR)&rexxexit },
X { "version", (APTR)&rexxversion },
X { "spawn", (APTR)&rexxspawn },
X { NULL, NULL } } ;
X#endif
X/*******************************************************************/
X/* DrawBox - Simple routine to draw an unfilled rectangle */
X/* It accepts the coordinates of the top-left and lower-right */
X/* points of the rectangle, a pointer to the Window structure, */
X/* and the color in which to render the rectangle. The current */
X/* FgPen color of the window is preserved thru the call. No */
X/* clipping is done. */
X/*******************************************************************/
Xvoid DrawBox( tlx, tly, brx, bry, window, color )
XSHORT tlx, tly; /* top-left x,y coordinates */
XSHORT brx, bry; /* lower-right x,y coordinates */
Xstruct Window *window; /* pointer to target window */
XBYTE color; /* color to use for render */
X {
X BYTE OldColor = window->RPort->FgPen; /* save window's FgPen */
X
X SetAPen( window->RPort, (long)color ); /* set draw color for box */
X Move(window->RPort, (long)tlx, (long)tly); /* move to top-left point */
X Draw(window->RPort, (long)brx, (long)tly); /* and draw to each of the */
X Draw(window->RPort, (long)brx, (long)bry); /* four corners of the box */
X Draw(window->RPort, (long)tlx, (long)bry);
X Draw(window->RPort, (long)tlx, (long)tly);
X SetAPen( window->RPort, (long)OldColor ); /* restore old FgPen */
X }
X
X
X/*********************************************************/
X/* Color Select Menu */
X/* */
X/* This is where the menu for color selection is */
X/* defined. It should be flexible enough to allow for */
X/* increased palette sizes, but this version is only */
X/* for the 4-color mode of the WorkBench screen. */
X/*********************************************************/
X
X/* A few definitions are needed here.
X * Note that MAXPAL should be increased
X * to allow for palette larger than
X * four colors.
X */
X#define ITEMSTUFF (ITEMENABLED | HIGHBOX)
X#define CW 40
X#define CH 25
X#define MAXPAL 4
X
X/* declare enough storage for required
X * number of menu items and associated
X * images. This menu will be using
X * graphics renditions of menu items,
X * so the Image structures must be
X * declared. This menu is modeled after
X * the one found in the IconEd source.
X */
Xstruct MenuItem coloritem[MAXPAL];
Xstruct Image colorimage[MAXPAL];
X
X/* array of palette sizes to correspond with
X * depth of window in bit-planes
X */
XSHORT palette[] = { 2, 4, 8, 16, 32 };
X
X
X/*****************************************************************/
X/* The following function initializes the structure arrays */
X/* needed to provide the Color menu topic. */
X/*****************************************************************/
XInitColorItems( depth )
XSHORT depth; /* number of bit-planes in window */
X {
X SHORT n, colors;
X
X colors = palette[depth-1];
X for( n=0; n<colors; n++ ) /* loop for max number of items */
X {
X coloritem[n].NextItem = &coloritem[n+1];
X coloritem[n].ItemFill = (APTR)&colorimage[n];
X /* the next two items might be changed for
X * when bit-planes is greater than 2
X */
X coloritem[n].LeftEdge = 2 + CW * (n % 4);
X coloritem[n].TopEdge = CH * (n / 4);
X coloritem[n].Width = CW;
X coloritem[n].Height = CH;
X coloritem[n].Flags = ITEMSTUFF;
X coloritem[n].MutualExclude = 0;
X coloritem[n].SelectFill = NULL;
X coloritem[n].Command = 0;
X coloritem[n].SubItem = NULL;
X coloritem[n].NextSelect = 0;
X
X colorimage[n].LeftEdge = 1;
X colorimage[n].TopEdge = 1;
X colorimage[n].Width = CW-2;
X colorimage[n].Height = CH-2;
X colorimage[n].Depth = depth;
X colorimage[n].ImageData = NULL;
X colorimage[n].PlanePick = 0;
X colorimage[n].PlaneOnOff = n;
X }
X coloritem[colors-1].NextItem = NULL; /* needed for last item in list */
X return( 0 );
X }
X
X
X/*****************************************************/
X/* Draw Mode Menu */
X/* */
X/* Here are the code and data declarations for */
X/* the DrawMode menu. Current choices are limited */
X/* to Erase, Filled Box, Hollow Box, and PenDraw. */
X/*****************************************************/
X
X/* define maximum number of menu items */
X#define DMODEMAX 4
X
X/* declare storage space for menu items and
X * their associated IntuiText structures
X */
Xstruct MenuItem DModeItem[DMODEMAX];
Xstruct IntuiText DModeText[DMODEMAX];
X
X/*****************************************************************/
X/* The following function initializes the structure arrays */
X/* needed to provide the DrawMode menu topic. */
X/*****************************************************************/
XInitDModeItems()
X {
X short n;
X
X /* initialize each meu item and IntuiText with loop */
X for( n=0; n<DMODEMAX; n++ )
X {
X DModeItem[n].NextItem = &DModeItem[n+1];
X DModeItem[n].LeftEdge = 0;
X DModeItem[n].TopEdge = 10 * n;
X DModeItem[n].Width = 112;
X DModeItem[n].Height = 10;
X DModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
X DModeItem[n].MutualExclude = 0;
X DModeItem[n].ItemFill = (APTR)&DModeText[n];
X DModeItem[n].SelectFill = NULL;
X DModeItem[n].Command = 0;
X DModeItem[n].SubItem = NULL;
X DModeItem[n].NextSelect = 0;
X
X DModeText[n].FrontPen = 0;
X DModeText[n].BackPen = 1;
X DModeText[n].DrawMode = JAM2; /* render in fore and background */
X DModeText[n].LeftEdge = 0;
X DModeText[n].TopEdge = 1;
X DModeText[n].ITextFont = NULL;
X DModeText[n].NextText = NULL;
X }
X DModeItem[DMODEMAX-1].NextItem = NULL;
X
X /* initialize text for specific menu items */
X DModeText[0].IText = (UBYTE *)"Erase All";
X DModeText[1].IText = (UBYTE *)"Hollow Box";
X DModeText[2].IText = (UBYTE *)"Filled Box";
X DModeText[3].IText = (UBYTE *)"Pen Draw";
X
X return( 0 );
X }
X
X
X/***************************************************/
X/* Menu Definition */
X/* */
X/* This section of code is where the simple */
X/* menu definition goes. For now it supports */
X/* only Color and Drawmode selection, but new */
X/* choices can easily be added by creating */
X/* structures and initializations functions */
X/* similar to those provided above. */
X/***************************************************/
X
X/* current number of available menu topics */
X#define MAXMENU 2
X
X/* declaration of menu structure array for
X * number of current topics. Intuition
X * will use the address of this array to
X * set and clear the menus associated with
X * the window.
X */
Xstruct Menu menu[MAXMENU];
X
X/**********************************************************************/
X/* The following function initializes the Menu structure array with */
X/* appropriate values for our simple menu strip. Review the manual */
X/* if you need to know what each value means. */
X/**********************************************************************/
XInitMenu()
X {
X menu[0].NextMenu = &menu[1];
X menu[0].LeftEdge = 10;
X menu[0].TopEdge = 0;
X menu[0].Width = 50;
X menu[0].Height = 10;
X menu[0].Flags = MENUENABLED;
X menu[0].MenuName = "Color"; /* text for menu-bar display */
X menu[0].FirstItem = &coloritem[0]; /* pointer to first item in list */
X
X menu[1].NextMenu = NULL;
X menu[1].LeftEdge = 65;
X menu[1].TopEdge = 0;
X menu[1].Width = 85;
X menu[1].Height = 10;
X menu[1].Flags = MENUENABLED;
X menu[1].MenuName = "DrawMode"; /* text for menu-bar display */
X menu[1].FirstItem = &DModeItem[0]; /* pointer to first item in list */
X
X return( 0 );
X }
X
X
X/******************************************************/
X/* Main Program */
X/* */
X/* This is the main body of the program. */
X/******************************************************/
X
Xstruct Window *Window; /* ptr to applications window */
XSHORT MinX, MinY, MaxX, MaxY; /* clipping boundary variables */
XSHORT KeepGoing = TRUE; /* main loop control value */
X
Xmain(argc, argv)
Xint argc ;
Xchar *argv[] ;
X {
X struct Library *OpenLibrary() ;
X struct Window *OpenWindow() ;
X struct Message *GetMsg() ;
X struct IntuiMessage *NewMessage; /* msg structure for GetMsg() */
X BYTE DrawColor = 1; /* initial drawing color */
X SHORT OldBRX = 30, OldBRY = 30; /* point coords used for boxes */
X SHORT TLX = 20, TLY = 20; /* initial top-left point coords */
X ULONG class; /* used in message monitor loop */
X USHORT code; /* used in message monitor loop */
X SHORT x, y, x1, y1, x2, y2; /* various coordinate variables */
X USHORT MenuNum, ItemNum;
X /* The following is a set of declarations
X * for a number of flag values used by the
X * program. These would perhaps be better
X * coded as a bit-field for all the flags,
X * but I'm lazy, and this is easier.
X */
X SHORT MouseMoved = FALSE; /* indicates new mouse position ready */
X SHORT ClipIt = FALSE; /* are new point coords out of bounds? */
X SHORT ClippedLast = FALSE; /* was last PenDraw operation clipped? */
X SHORT PenMode = FALSE; /* indicates PenDraw mode is set */
X SHORT PenDown = FALSE; /* if mouse moved, then should it draw? */
X SHORT RubberBox = FALSE; /* are we currently rubberbanding a box? */
X SHORT FilledBox = FALSE; /* should boxes be filled when drawn? */
X#ifdef TALKTOREXX
X/*
X * If we are talking to REXX, we need these two additional locals.
X */
X long rexxbit ;
X char firstcommand[256] ;
X#endif
X
X /* attempt to Open Library to access Intuition */
X IntuitionBase = (struct IntuitionBase *)
X OpenLibrary("intuition.library", INTUITION_REV);
X if( IntuitionBase == NULL )
X exit(FALSE);
X
X /* attempt to OpenLibrary to access Graphics functions */
X GfxBase = (struct GfxBase *)
X OpenLibrary("graphics.library",GRAPHICS_REV);
X if( GfxBase == NULL )
X exit(FALSE);
X
X
X /* Try to open new window for application */
X if(( Window = OpenWindow(&NewWindow) ) == NULL)
X exit(FALSE);
X
X /* set initial clipping boundaries
X * from the values found in the window
X * structure for border dimensions
X */
X MinX = Window->BorderLeft;
X MinY = Window->BorderTop;
X MaxX = Window->Width - Window->BorderRight - 1;
X MaxY = Window->Height - Window->BorderBottom - 1;
X
X InitColorItems( 2 ); /* initialize Color menu arrays */
X InitDModeItems(); /* initialize DrawMode menu arrays */
X InitMenu(); /* initialize the menu structures */
X
X /* Now, having initialized the various arrays
X * of structures required for menu generation
X * we can tell Intuition to make our menus
X * available to the user when this window
X * is active.
X */
X SetMenuStrip( Window, &menu[0] );
X
X /* set initial drw mode and color */
X SetDrMd( Window->RPort, JAM1 );
X SetAPen( Window->RPort, DrawColor );
X#ifdef TALKTOREXX
X/*
X * For rexx, we open up a Rexx port, and send out the first command,
X * if there was one. We send it out asynchronously; no reason not to.
X */
X rexxbit = upRexxPort("freedraw", rcl, "fd", &disp) ;
X firstcommand[0] = 0 ;
X for (x=1; x<argc; x++) {
X strcat(firstcommand, argv[x]) ;
X strcat(firstcommand, " ") ;
X }
X if (firstcommand[0]) {
X asyncRexxCmd(firstcommand) ;
X }
X#endif
X /* Everything the program needs is now
X * initialized and put in place. The
X * program enters the following loop
X * and processes message continuously as
X * they are received from Intuition.
X * I guess this loop is the real workhorse
X * of the program. By the way, the loop
X * control variable KeepGoing remains TRUE
X * until a CLOSEWINDOW message is received.
X * At that point it goes FALSE, and the
X * program cleans up and exits.
X */
X while( KeepGoing )
X {
X
X /* stay here until a message is received from Intuition */
X#ifdef TALKTOREXX
X/*
X * If we're working with Rexx, we wait on the Rexx bit as well.
X * Then, we handle any Rexx messages.
X */
X Wait( (1L << Window->UserPort->mp_SigBit) | rexxbit);
X dispRexxPort() ;
X#else
X Wait( 1L << Window->UserPort->mp_SigBit);
X#endif
X MouseMoved = FALSE; /* clear this flag each time thru loop */
X
X /* since more than one message may be waiting
X * a reply at this point, a loop is used to
X * process all that have come in until no more
X * are ready. Msg received is assigned to
X * NewMessage from the GetMsg() function. This
X * value will be NULL if no message is ready,
X * and control passes out of the loop at that time
X */
X while( NewMessage=(struct IntuiMessage *)GetMsg(Window->UserPort) )
X {
X
X /* copy some values from the message structure
X * to variables used in the switch statements
X * below
X */
X class = NewMessage->Class;
X code = NewMessage->Code;
X x = Window->MouseX;
X y = Window->MouseY;
X
X /* SIZEVERIFY is a very high priority message
X * in our loop and requires some immediate
X * servicing. Any outstanding draw operations
X * are immediately cancelled, and the DrawMode
X * is nulled. This prevents any attempts to
X * render outside whatever new Window boundaries
X * the user chooses.
X *
X * (not anymore, it don't. -tgr)
X
X if( class == SIZEVERIFY )
X {
X PenDown = FALSE;
X if( RubberBox )
X {
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X RubberBox = FALSE;
X }
X }
X */
X
X /* we have all the information needed from
X * the message, so we can now safely reply
X * to it without losing data
X */
X ReplyMsg( NewMessage );
X
X /* Examine point coords from message received
X * and set the clipping flag if out of bounds.
X * If user was drawing in PenMode when message
X * was received, then the ClippedLast flag
X * should also be set to indicate this to the
X * next draw operation.
X */
X if(ClipIt = ( x < MinX || x > MaxX || y < MinY || y > MaxY ))
X if( PenDown )
X ClippedLast = TRUE;
X
X
X /* enter switch on type of message received */
X switch( class )
X {
X case MOUSEMOVE:
X /* Don't really do anything with this one
X * until any other, more important, messages
X * are received and processed.
X */
X MouseMoved = TRUE;
X break;
X
X case NEWSIZE:
X /* set new clipping boundaries */
X MinX = Window->BorderLeft;
X MinY = Window->BorderTop;
X MaxX = Window->Width - Window->BorderRight - 1;
X MaxY = Window->Height - Window->BorderBottom - 1;
X break;
X
X case CLOSEWINDOW:
X /* User is ready to quit, so indicate
X * that execution should terminate
X * with next iteration of the loop.
X */
X KeepGoing = FALSE;
X break;
X
X case MOUSEBUTTONS:
X /* A number of things could have happened
X * here, and further examination of data
X * received from message is needed to
X * determine what action should be taken.
X * The code variable holds important info
X * about what actually caused the message
X * to be sent in the first place.
X */
X switch ( code )
X {
X case SELECTUP:
X /* User was holding down the left button
X * and just released it. The PenMode
X * flag variables are set accordingly.
X * The pen can no longer be down, and
X * ClippedLast is reset for next time.
X */
X PenDown = ClippedLast = FALSE;
X break;
X
X case SELECTDOWN:
X /* User has pressed the left button, and
X * several differnt actions may need to
X * be taken. If the ClipIt value is TRUE,
X * then no action should be taken at all.
X */
X if( ClipIt )
X break;
X
X /* If user is currently in PenMode, then
X * set up to draw when MOUSEMOVED messages
X * are received until a subsequent SELECTUP
X * message comes in.
X */
X if( PenMode )
X {
X PenDown = TRUE;
X ClippedLast = FALSE;
X
X /* make sure to set appropriate mode */
X SetDrMd( Window->RPort, JAM1 );
X
X /* and establish initial position to draw */
X Move( Window->RPort, (long)x, (long)y );
X break;
X }
X
X /* If user is currently rubberbanding a box,
X * then a SELECTDOWN message means it is time
X * to stop rubberbanding and actually draw it.
X * The following code will be executed if
X * this is the case, and it will determine if
X * a filled box is needed before rendering.
X */
X if( RubberBox )
X {
X /* set draw mode back to JAM1 since
X * it is now currently set to COMPLEMENT
X */
X SetDrMd( Window->RPort, JAM1 );
X RubberBox = FALSE; /* turn off rubberbanding */
X
X /* Restore the condition of the RMBTRAP
X * bit in the Window structure's Flags
X * member. Menubutton events will no
X * be received by this loop.
X */
X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X
X /* RectFill is not condusive to the smooth
X * execution of programs iit arguments are
X * out of order, sot his code sorts them
X * in preparation for the call.
X */
X if( FilledBox )
X {
X /* first sort the x-coords */
X if( TLX < OldBRX ) {
X x1 = TLX; x2 = OldBRX; }
X else {
X x1 = OldBRX; x2 = TLX; }
X
X /* then sort the y-coords */
X if( TLY < OldBRY ) {
X y1 = TLY; y2 = OldBRY; }
X else {
X y1 = OldBRY; y2 = TLY; }
X
X /* now generate the filled rectangle */
X RectFill( Window->RPort, (long)x1, (long)y1,
X (long)x2, (long)y2 );
X }
X else
X {
X /* FilledBox not set, so draw hollow box */
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X }
X break;
X }
X
X /* If execution comes here, then PenMode was
X * not set and user was not rubberbanding.
X * SELECTDOWN therefore indicates to start the
X * rubberbanding process at this point. The
X * initial coords are set to the values we
X * received when the GetMsg() was executed.
X */
X TLX = OldBRX = x; TLY = OldBRY = y;
X
X /* set to render in XOR mode */
X SetDrMd( Window->RPort, COMPLEMENT );
X
X /* set flag to indicate we are now rubberbanding */
X RubberBox = TRUE;
X
X /* This instruction indicates to Intuition
X * that we now wish to receive a message
X * each time the Menubutton is pressed.
X * This is how we hijack the right button
X * for temporary use as a Cancel button
X * instead of a Menubutton.
X */
X Window->Flags |= RMBTRAP;
X
X /* render the initial rubberbox and exit */
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X break;
X
X case MENUDOWN:
X /* WE only receive this message class if
X * the RMBTRAP flag bit has been set, so
X * it always means that we should cancel
X * the box which is currently rubberbanding.
X */
X /* turn the flag off */
X RubberBox = FALSE;
X
X /* restore control of menubutton to Intuition */
X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X
X /* erase (by double XOR'ing) the current
X * rubberbox and exit switch.
X */
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X break;
X
X default:
X /* Something unimportant happened, so just
X * continue thru the GetMsg() loop.
X */
X continue;
X }
X break;
X
X case MENUPICK:
X /* A menu event has taken place and is
X * ready to be processed. Examine the
X * code variable received from the message
X * to determine what action should be taken.
X * The first check is for MENUNULL, which
X * means that nothing should be done at all.
X */
X if( code != MENUNULL )
X {
X /* get menu and item numbers from code */
X MenuNum = MENUNUM( code );
X ItemNum = ITEMNUM( code );
X
X /* determine appropriate action by menu number */
X switch ( MenuNum )
X {
X case 0:
X /* Menu 0 is the Color menu. The
X * item number indicates which new
X * color to set.
X */
X DrawColor = ItemNum;
X SetAPen( Window->RPort, (long)DrawColor );
X break;
X
X case 1:
X /* Menu 1 is the DrawMode menu. The item
X * number indicates what to do.
X * NOTE: Since we cannot have received
X * this message if we were rubberbanding,
X * then there is no need to clean up before
X * changing drawing modes.
X */
X switch ( ItemNum )
X {
X case 0:
X /* Erase window to current color */
X SetDrMd( Window->RPort, JAM1 );
X RectFill( Window->RPort, (long)MinX, (long)MinY,
X (long)MaxX, (long)MaxY);
X break;
X
X case 1:
X /* set flag variables for hollow box */
X PenMode = FALSE;
X FilledBox = FALSE;
X break;
X
X case 2:
X /* set flag variables for filled box */
X PenMode = FALSE;
X FilledBox = TRUE;
X break;
X
X case 3:
X /* set flag variables for PenMode */
X PenMode = TRUE;
X break;
X
X default:
X /* don't do anything */
X break;
X }
X break;
X
X default:
X /* Menu number unrecognized, do nothing */
X break;
X }
X }
X break;
X
X case INACTIVEWINDOW:
X /* User has de-selected our window, so a
X * little bit of cleaning up may be needed
X * to prevent untoward events when he comes
X * back to it.
X */
X /* erase any outstanding rubberbox */
X if( RubberBox )
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X
X /* reset all the flafg variables */
X PenDown = ClippedLast = RubberBox = FALSE;
X
X /* return possibly diverted menubutton events to Big I */
X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X break;
X
X default:
X /* message class was unrecognized, so do nothing */
X break;
X }
X } /* this brace ends the while(NewMessage) loop way back when */
X
X /* There are no more messages waiting at the
X * IDCMP port, so we can now proceed to
X * process any MOUSEMOVED message we may
X * have received.
X */
X if( MouseMoved && !ClipIt)
X {
X /* the mouse did move, and we don't need to clip */
X
X /* check first if we are drawing in PenMode */
X if( PenDown )
X {
X /* We have to examine if we clipped the
X * last PenMode draw operation. If we did,
X * then this is the first move back into
X * window boundaries, so we mov instead of
X * drawing.
X */
X if( ClippedLast )
X {
X ClippedLast = FALSE; /* reset this flag now */
X Move( Window->RPort, (long)x, (long)y );
X }
X else
X Draw( Window->RPort, (long)x, (long)y ); /* draw to x,y coords */
X }
X else
X {
X /* We weren't in PenMode, but we still might
X * be rubberbanding a box. If so, then we
X * should erase the current rubberbox and
X * draw a new one with the new mouse coords.
X */
X if( RubberBox )
X {
X /* erase the old rubberbox - draw mode is COMPLEMENT */
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X
X /* assign new values to box coords */
X OldBRX = x; OldBRY = y;
X
X /* and draw the new rubberbox */
X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X }
X }
X }
X }
X
X /* It must be time to quit, so we have to clean
X * up and exit.
X */
X#ifdef TALKTOREXX
X/*
X * With Rexx, we need to bring the port down. You might make this
X * part of exit() for programs that have multiple paths to exit.
X */
X dnRexxPort() ;
X#endif
X ClearMenuStrip( Window );
X CloseWindow( Window );
X exit(TRUE);
X }
X#ifdef TALKTOREXX
X/*
X * Now we get into the actual code necessary for our REXX port; functions
X * that do the real work. Note that this program was not structured
X * particularly nicely for Rexx; I had to write each of these functions.
X * Many programs have these subroutines already in place; they are called
X * as part of the event loop. This progam, however, just has one big
X * switch statement with different actions . . .
X *
X * First, our locals.
X */
Xint currrexxcolor = 1 ; /* what color is *rexx* drawing in? */
Xint args[4] ; /* what args did we see to this function? */
Xint parsed ; /* was argument parsing successful? */
Xint userreplied ; /* has the current message been replied to yet? */
X/*
X * This function takes a pointer to a pointer to a string, grabs the
X * next number, returns it, and advances the pointer to the string to
X * point after the number.
X */
Xint getnm(where)
Xchar **where ;
X{
X register char *p = *where ;
X register int val = 0 ;
X int gotone = 0 ;
X
X while (*p <= ' ' && *p)
X p++ ;
X while ('0' <= *p && *p <= '9') {
X gotone = 1 ;
X val = 10 * val + *p++ - '0' ;
X }
X if (gotone == 0)
X parsed = 0 ;
X *where = p ;
X return(val) ;
X}
X/*
X * This function trys to find `n' numeric arguments in the command
X * string, and stuffs them into the args array.
X */
Xvoid parseargs(p, n)
Xchar *p ;
Xint n ;
X{
X register int i ;
X
X while (*p > ' ' && *p)
X p++ ;
X for (i=0; i<n; i++)
X args[i] = getnm(&p) ;
X}
X/*
X * This is our main dispatch function. We check to make sure a Window
X * currently exists. Then, we store away the `current color' and change
X * it to Rexx's current color, call our handler function, and then restore
X * the color. If our handler replied, we return a 1 to indicate that.
X * If the parse and everything else was successful, we return a 0.
X * Otherwise, we return a failure of 20 to indicate that the arguments
X * were messed up.
X */
Xint disp(msg, dat, p)
Xregister struct RexxMsg *msg ;
Xregister struct rexxCommandList *dat ;
Xchar *p ;
X{
X register int t ;
X
X parsed = 1 ;
X if (Window) {
X userreplied = 0 ;
X t = Window->RPort->FgPen ;
X SetAPen(Window->RPort, (long)currrexxcolor) ;
X ((int (*)())(dat->userdata))(msg, p) ;
X SetAPen(Window->RPort, (long)t) ;
X if (! parsed)
X replyRexxCmd(msg, (long)parsed, 0L, NULL) ;
X return ;
X }
X replyRexxCmd(msg, 20L, 10L, NULL) ;
X}
X/*
X * This handler sets the current rexx color.
X */
Xvoid rexxcolor(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X parseargs(p, 1) ;
X currrexxcolor = args[0] ;
X}
X/*
X * This function silently clips the x and y values at `n' to the
X * window bounds.
X */
Xvoid clipxy(n)
Xint n ;
X{
X if (args[n] < MinX)
X args[n] = MinX ;
X if (args[n] > MaxX)
X args[n] = MaxX ;
X n++ ;
X if (args[n] < MinY)
X args[n] = MinY ;
X if (args[n] > MaxY)
X args[n] = MaxY ;
X}
X/*
X * This handler grabs four arguments and draws a box.
X */
Xvoid rexxbox(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X parseargs(p, 4) ;
X clipxy(0) ;
X clipxy(2) ;
X DrawBox(args[0], args[1], args[2], args[3], Window, currrexxcolor) ;
X}
X/*
X * This handler grabs four arguments and draws a filled box.
X */
Xvoid rexxfbox(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X register int t ;
X
X parseargs(p, 4) ;
X clipxy(0) ;
X clipxy(2) ;
X if (args[0] > args[2]) {
X t = args[0] ; args[0] = args[2] ; args[2] = t ;
X }
X if (args[1] > args[3]) {
X t = args[1] ; args[1] = args[3] ; args[3] = t ;
X }
X RectFill( Window->RPort, (long)args[0], (long)args[1],
X (long)args[2], (long)args[3]) ;
X}
X/*
X * This handler grabs four arguments and draws a line.
X */
Xvoid rexxline(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X parseargs(p, 4) ;
X clipxy(0) ;
X clipxy(2) ;
X Move(Window->RPort, (long)args[0], (long)args[1]) ;
X Draw(Window->RPort, (long)args[2], (long)args[3]) ;
X}
X/*
X * This handler pops the window to front.
X */
Xvoid rexxtofront(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X WindowToFront(Window) ;
X}
X/*
X * This handler pops the window to back.
X */
Xvoid rexxtoback(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X WindowToBack(Window) ;
X}
X/*
X * This handler sets the exit flag.
X */
Xvoid rexxexit(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X KeepGoing = 0 ;
X}
X/*
X * This handler returns the version of the program.
X */
Xvoid rexxversion(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X userreplied = 1 ;
X replyRexxCmd(msg, 0L, 0L, VERSION) ;
X}
X/*
X * This handler sends the rest of the command asynchronously,
X * allowing us to run macros in parallel.
X */
Xvoid rexxspawn(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X while (*p <= ' ' && *p)
X p++ ;
X asyncRexxCmd(p) ;
X}
X#endif
SHAR_EOF
echo "extracting minrexx.c"
sed 's/^X//' << \SHAR_EOF > minrexx.c
X/*
X * This is an example of how REXX messages might be handled. This is
X * a `minimum' example that both accepts asynchronous REXX messages and
X * can request REXX service.
X *
X * Read this entire file! It's short enough.
X *
X * It is written in such a fashion that it can be attached to a program
X * with a minimum of fuss. The only external symbols it makes available
X * are the seven functions and RexxSysBase.
X *
X * This code is by Radical Eye Software, but it is put in the public
X * domain. I would appreciate it if the following string was left in
X * both as a version check and as thanks from you for the use of this
X * code.
X *
X * If you modify this file for your own use, don't bump the version
X * number; add a suffix, such as 1.0a or 1.0.3 or something, so we
X * don't have fake `versions' floating around.
X */
Xstatic char *blurb = "Radical Eye MinRexx 0.4" ;
X/*
X * We read in our own personal little include.
X */
X#include "minrexx.h"
X/*
X * All of our local globals, hidden from sight.
X */
Xstatic struct MsgPort *rexxPort ; /* this is *our* rexx port */
Xstatic int bringerdown ; /* are we trying to shut down? */
Xstatic struct rexxCommandList *globalrcl ; /* our command association list */
Xstatic long stillNeedReplies ; /* how many replies are pending? */
Xstatic long rexxPortBit ; /* what bit to wait on for Rexx? */
Xstatic char *extension ; /* the extension for macros */
Xstatic int (*userdisp)() ; /* the user's dispatch function */
Xstatic struct RexxMsg *oRexxMsg ; /* the outstanding Rexx message */
X/*
X * Our library base. Don't you dare close this!
X */
Xstruct RxsLib *RexxSysBase ;
X/*
X * This is the main entry point into this code.
X */
Xlong upRexxPort(s, rcl, exten, uf)
X/*
X * The first argument is the name of your port to be registered;
X * this will be used, for instance, with the `address' command of ARexx.
X */
Xchar *s ;
X/*
X * The second argument is an association list of command-name/user-data
X * pairs. It's an array of struct rexxCommandList, terminated by a
X * structure with a NULL in the name field. The commands are case
X * sensitive. The user-data field can contain anything appropriate,
X * perhaps a function to call or some other data.
X */
Xstruct rexxCommandList *rcl ;
X/*
X * The third argument is the file extension for ARexx macros invoked
X * by this program. If you supply this argument, any `primitive' not
X * in the association list rcl will be sent out to ARexx for
X * interpretation, thus allowing macro programs to work just like
X * primitives. If you do not want this behavior, supply a `NULL'
X * here, and those commands not understood will be replied with an
X * error value of RXERRORNOCMD.
X */
Xchar *exten ;
X/*
X * The fourth argument is the user dispatch function. This function
X * will *only* be called from rexxDisp(), either from the user calling
X * this function directly, or from dnRexxPort(). Anytime a command
X * match is found in the association list, this user-supplied function
X * will be called with two arguments---the Rexx message that was
X * received, and a pointer to the association pair. This function
X * should return a `1' if the message was replied to by the function
X * and a `0' if the default success code of (0, 0) should be returned.
X * Note that the user function should never ReplyMsg() the message;
X * instead he should indicate the return values with replyRexxCmd();
X * otherwise we lose track of the messages that still lack replies.
X */
Xint (*uf)() ;
X/*
X * upRexxPort() returns the signal bit to wait on for Rexx messages.
X * If something goes wrong, it simply returns a `0'. Note that this
X * function is safe to call multiple times because we check to make
X * sure we haven't opened already. It's also a quick way to change
X * the association list or dispatch function.
X */
X{
X struct MsgPort *FindPort() ;
X struct MsgPort *CreatePort() ;
X
X/*
X * Some basic error checking.
X */
X if (rcl == NULL || uf == NULL)
X return(0L) ;
X/*
X * If we aren't open, we make sure no one else has opened a port with
X * this name already. If that works, and the createport succeeds, we
X * fill rexxPortBit with the value to return.
X *
X * Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
X * for rexxPort == NULL also insures that our rexxPortBit is 0.
X */
X if (rexxPort == NULL) {
X Forbid() ;
X if (FindPort(s)==NULL)
X rexxPort = CreatePort(s, 0L) ;
X Permit() ;
X if (rexxPort != NULL)
X rexxPortBit = 1L << rexxPort->mp_SigBit ;
X }
X/*
X * Squirrel away these values for our own internal access, and return
X * the wait bit.
X */
X globalrcl = rcl ;
X extension = exten ;
X userdisp = uf ;
X return(rexxPortBit) ;
X}
X/*
X * This function closes the rexx library, but only if it is open
X * and we aren't expecting further replies from REXX. It's
X * *private*, but it doesn't have to be; it's pretty safe to
X * call anytime.
X */
Xstatic void closeRexxLib() {
X if (stillNeedReplies == 0 && RexxSysBase) {
X CloseLibrary(RexxSysBase) ;
X RexxSysBase = NULL ;
X }
X}
X/*
X * This function closes down the Rexx port. It is always safe to
X * call, and should *definitely* be made a part of your cleanup
X * routine. No arguments and no return. It removes the Rexx port,
X * replies to all of the messages and insures that we get replies
X * to all the ones we sent out, closes the Rexx library, deletes the
X * port, clears a few flags, and leaves.
X */
Xvoid dnRexxPort() {
X if (rexxPort) {
X RemPort(rexxPort) ;
X bringerdown = 1 ;
X/*
X * A message still hanging around? We kill it off.
X */
X if (oRexxMsg) {
X oRexxMsg->rm_Result1 = RXERRORIMGONE ;
X ReplyMsg(oRexxMsg) ;
X oRexxMsg = NULL ;
X }
X while (stillNeedReplies) {
X WaitPort(rexxPort) ;
X dispRexxPort() ;
X }
X closeRexxLib() ;
X DeletePort(rexxPort) ;
X rexxPort = NULL ;
X }
X rexxPortBit = 0 ;
X}
X/*
X * Here we dispatch any REXX messages that might be outstanding.
X * This is the main routine for handling Rexx messages.
X * This function is fast if no messages are outstanding, so it's
X * pretty safe to call fairly often.
X *
X * If we are bring the system down and flushing messages, we reply
X * with a pretty serious return code RXERRORIMGONE.
X *
X * No arguments, no returns.
X */
Xvoid dispRexxPort() {
X register struct RexxMsg *GetMsg() ;
X register struct RexxMsg *RexxMsg ;
X int cmdcmp() ;
X register struct rexxCommandList *rcl ;
X register char *p ;
X register int dontreply ;
X
X/*
X * If there's no rexx port, we're out of here.
X */
X if (rexxPort == NULL)
X return ;
X/*
X * Otherwise we have our normal loop on messages.
X */
X while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
X/*
X * If we have a reply to a message we sent, we look at the second
X * argument. If it's set, it's a function we are supposed to call
X * so we call it. Then, we kill the argstring and the message
X * itself, decrement the outstanding count, and attempt to close
X * down the Rexx library. Note that this call only succeeds if
X * there are no outstanding messages. Also, it's pretty quick, so
X * don't talk to me about efficiency.
X */
X if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
X if (RexxMsg->rm_Args[1]) {
X ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ;
X }
X DeleteArgstring(RexxMsg->rm_Args[0]) ;
X DeleteRexxMsg(RexxMsg) ;
X stillNeedReplies-- ;
X closeRexxLib() ;
X/*
X * The default case is we got a message and we need to check it for
X * primitives. We skip past any initial tabs or spaces and initialize
X * the return code fields.
X */
X } else {
X p = (char *)RexxMsg->rm_Args[0] ;
X while (*p > 0 && *p <= ' ')
X p++ ;
X RexxMsg->rm_Result1 = 0 ;
X RexxMsg->rm_Result2 = 0 ;
X/*
X * If somehow the reply is already done or postponed, `dontreply' is
X * set.
X */
X dontreply = 0 ;
X/*
X * If the sky is falling, we just blow up and replymsg.
X */
X if (bringerdown) {
X RexxMsg->rm_Result1 = RXERRORIMGONE ;
X/*
X * Otherwise we cdr down our association list, comparing commands,
X * until we get a match. If we get a match, we call the dispatch
X * function with the appropriate arguments, and break out.
X */
X } else {
X oRexxMsg = RexxMsg ;
X for (rcl = globalrcl; rcl->name; rcl++) {
X if (cmdcmp(rcl->name, p) == 0) {
X userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
X break ;
X }
X }
X/*
X * If we broke out, rcl will point to the command we executed; if we
X * are at the end of the list, we didn't understand the command. In
X * this case, if we were supplied an extension in upRexxPort, we know
X * that we should send the command out, so we do so, synchronously.
X * The synchronous send takes care of our reply. If we were given a
X * NULL extension, we bitch that the command didn't make sense to us.
X */
X if (rcl->name == NULL) {
X if (extension) {
X syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
X dontreply = 1 ;
X } else {
X RexxMsg->rm_Result1 = RXERRORNOCMD ;
X }
X }
X }
X/*
X * Finally, reply if appropriate.
X */
X oRexxMsg = NULL ;
X if (! dontreply)
X ReplyMsg(RexxMsg) ;
X }
X }
X}
X/*
X * This is the function we use to see if the command matches
X * the command string. Not case sensitive, and the real command only
X * need be a prefix of the command string. Make sure all commands
X * are given in lower case!
X */
Xstatic int cmdcmp(c, m)
Xregister char *c, *m ;
X{
X while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
X c++ ;
X m++ ;
X }
X return(*c) ;
X}
X/*
X * Opens the Rexx library if unopened. Returns success (1) or
X * failure (0). This is another function that is *private* but
X * that doesn't have to be.
X */
Xstatic int openRexxLib() {
X struct RxsLib *OpenLibrary() ;
X
X if (RexxSysBase)
X return(1) ;
X return((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL) ;
X}
X/*
X * This is the general ARexx command interface, but is not the one
X * you will use most of the time; ones defined later are easier to
X * understand and use. But they all go through here.
X */
Xstruct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
Xchar *s ;
X/*
X * The first parameter is the command to send to Rexx.
X */
Xint (*f)() ;
X/*
X * The second parameter is either NULL, indicating that the command
X * should execute asynchronously, or a function to be called when the
X * message we build up and send out here finally returns. Please note
X * that the function supplied here could be called during cleanup after
X * a fatal error, so make sure it is `safe'. This function always is
X * passed one argument, the RexxMsg that is being replied.
X */
XSTRPTR p1, p2, p3 ;
X/*
X * These are up to three arguments to be stuffed into the RexxMsg we
X * are building up, making the values available when the message is
X * finally replied to. The values are stuffed into Args[2]..Args[4].
X */
X{
X struct RexxMsg *CreateRexxMsg() ;
X STRPTR CreateArgstring() ;
X register struct MsgPort *rexxport ;
X register struct RexxMsg *RexxMsg ;
X
X/*
X * If we have too many replies out there, we just return failure.
X * Note that you should check the return code to make sure your
X * message got out! Then, we forbid, and make sure that:
X * - we have a rexx port open
X * - Rexx is out there
X * - the library is open
X * - we can create a message
X * - we can create an argstring
X *
X * If all of these succeed, we stuff a few values and send the
X * message, permit, and return.
X */
X if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
X return(NULL) ;
X RexxMsg = NULL ;
X if (openRexxLib() && (RexxMsg =
X CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
X (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
X RexxMsg->rm_Action = RXCOMM ;
X RexxMsg->rm_Args[1] = (STRPTR)f ;
X RexxMsg->rm_Args[2] = p1 ;
X RexxMsg->rm_Args[3] = p2 ;
X RexxMsg->rm_Args[4] = p3 ;
X RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
X Forbid() ;
X if (rexxport = FindPort(RXSDIR))
X PutMsg(rexxport, RexxMsg) ;
X Permit() ;
X if (rexxport) {
X stillNeedReplies++ ;
X return(RexxMsg) ;
X } else
X DeleteArgstring(RexxMsg->rm_Args[0]) ;
X }
X if (RexxMsg)
X DeleteRexxMsg(RexxMsg) ;
X closeRexxLib() ;
X return(NULL) ;
X}
X/*
X * This function is used to send out an ARexx message and return
X * immediately. Its single parameter is the command to send.
X */
Xstruct RexxMsg *asyncRexxCmd(s)
Xchar *s ;
X{
X return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
X}
X/*
X * This function sets things up to reply to the message that caused
X * it when we get a reply to the message we are sending out here.
X * But first the function we pass in, which actually handles the reply.
X * Note how we get the message from the Args[2]; Args[0] is the command,
X * Args[1] is this function, and Args[2]..Args[4] are any parameters
X * passed to sendRexxCmd() as p1..p3. We pass the result codes right
X * along.
X */
Xstatic void replytoit(msg)
Xregister struct RexxMsg *msg ;
X{
X register struct RexxMsg *omsg ;
X
X omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
X replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
X ReplyMsg(omsg) ;
X}
X/*
X * This function makes use of everything we've put together so far,
X * and functions as a synchronous Rexx call; as soon as the macro
X * invoked here returns, we reply to `msg', passing the return codes
X * back.
X */
Xstruct RexxMsg *syncRexxCmd(s, msg)
Xchar *s ;
Xstruct RexxMsg *msg ;
X{
X return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ;
X}
X/*
X * There are times when you want to pass back return codes or a
X * return string; call this function when you want to do that,
X * and return `1' from the user dispatch function so the main
X * event loop doesn't reply (because we reply here.) This function
X * always returns 1.
X */
Xvoid replyRexxCmd(msg, primary, secondary, string)
X/*
X * The first parameter is the message we are replying to.
X */
Xregister struct RexxMsg *msg ;
X/*
X * The next two parameters are the primary and secondary return
X * codes.
X */
Xregister long primary, secondary ;
X/*
X * The final parameter is a return string. This string is only
X * returned if the primary return code is 0, and a string was
X * requested.
X *
X * We also note that we have replied to the message that came in.
X */
Xregister char *string ;
X{
X STRPTR CreateArgstring() ;
X
X/*
X * Note how we make sure the Rexx Library is open before calling
X * CreateArgstring . . . and we close it down at the end, if possible.
X */
X if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
X if (string && openRexxLib())
X secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
X else
X secondary = 0L ;
X }
X msg->rm_Result1 = primary ;
X msg->rm_Result2 = secondary ;
X closeRexxLib() ;
X}
SHAR_EOF
echo "extracting minrexx.h"
sed 's/^X//' << \SHAR_EOF > minrexx.h
X/*
X * Includes for minrexx.c; please refer to that file for
X * further documentation.
X */
X#include <rexx/rxslib.h>
X/*
X * This is the list of functions we can access. (Cheap forward
X * declarations, too.)
X */
Xlong upRexxPort() ;
Xvoid dnRexxPort() ;
Xvoid dispRexxPort() ;
Xstruct RexxMsg *sendRexxCmd() ;
Xstruct RexxMsg *syncRexxCmd() ;
Xstruct RexxMsg *asyncRexxCmd() ;
Xvoid replyRexxCmd() ;
X/*
X * Maximum messages that can be pending, and the return codes
X * for two bad situations.
X */
X#define MAXRXOUTSTANDING (300)
X#define RXERRORIMGONE (100)
X#define RXERRORNOCMD (30)
X/*
X * This is the association list you build up (statically or
X * dynamically) that should be terminated with an entry with
X * NULL for the name . . .
X */
Xstruct rexxCommandList {
X char *name ;
X APTR userdata ;
X} ;
SHAR_EOF
echo "extracting Makefile"
sed 's/^X//' << \SHAR_EOF > Makefile
Xall: freedraw origfreedraw
X
Xfreedraw: freedraw.o minrexx.o rexxglue.o
X ln freedraw.o minrexx.o rexxglue.o -lc
X
Xfreedraw.o: freedraw.c minrexx.h
X cc -DTALKTOREXX freedraw.c
X
Xminrexx.o: minrexx.c minrexx.h
X cc minrexx.c
X
Xorigfreedraw: origfreedraw.o
X ln origfreedraw.o -lc
X
Xorigfreedraw.o: freedraw.c
X cc -o origfreedraw.o freedraw.c
X
Xclean:
X delete freedraw.o origfreedraw.o freedraw origfreedraw minrexx.o
SHAR_EOF
echo "extracting freeedraw.uu"
sed 's/^X//' << \SHAR_EOF > freeedraw.uu
X
Xbegin 700 freedraw
XM```#\P`````````#``````````(```?Q````QP````$```/I```'\4[Z%L9&)
XM<F5E9')A=R`P+C`Q(&)Y(%)I8VAA<F0@32X@4F]S<P``04U)1T$@1G)E941RZ
XM87<@,"XP,0!C;VQO<@!B;W@`9F)O>`!L:6YE`'1O9G)O;G0`=&]B86-K`&5X3
XM:70`=F5R<VEO;@!S<&%W;@!.5?_^(&T`$")H`#(;:0`9__\0+0`52(!(P"\`Q
XM(&T`$"\H`#).NAZ>4$\P+0`*2,`O`#`M``A(P"\`(&T`$"\H`#).NAY<3^\`I
XM##`M``I(P"\`,"T`#$C`+P`@;0`0+R@`,DZZ'BI/[P`,,"T`#DC`+P`P+0`,5
XM2,`O`"!M`!`O*``R3KH>"D_O``PP+0`.2,`O`#`M``A(P"\`(&T`$"\H`#).)
XMNAWJ3^\`##`M``I(P"\`,"T`"$C`+P`@;0`0+R@`,DZZ'<I/[P`,$"W__TB`G
XM2,`O`"!M`!`O*``R3KH=YE!/3EU.=4Y5__PP+0`(4T!(P..`0>R`ACMP"`#_;
XM_$)M__Y@``&6,"W__L'\`")![(#(,BW__E)!P_P`(D/L@,C2B2&!"``P+?_^S
XMP?P`(D'L@-HR+?_^P_P`%$/L@5#2B2&!"``P+?_^P?P`(D'L@,PR+?_^2,&#T
XM_``$2$'#_``H5$$Q@0@`,"W__L'\`")![(#.,BW__DC!@_P`!,/\`!DQ@0@`M
XM,"W__L'\`")![(#0,;P`*`@`,"W__L'\`")![(#2,;P`&0@`,"W__L'\`")!3
XM[(#4,;P`D`@`,"W__L'\`")![(#60K`(`#`M__[!_``B0>R`WD*P"``P+?_^3
XMP?P`(D'L@.)",`@`,"W__L'\`")![(#D0K`(`#`M__[!_``B0>R`Z$)P"``PD
XM+?_^P?P`%$'L@5`QO``!"``P+?_^P?P`%$'L@5(QO``!"``P+?_^P?P`%$'LH
XM@50QO``F"``P+?_^P?P`%$'L@58QO``7"``P+?_^P?P`%$'L@5@QK0`("``PZ
XM+?_^P?P`%$'L@5I"L`@`,"W__L'\`!1![(%>0C`(`#`M__[!_``40>R!7Q&MD
XM__\(`%)M__XP+?_^L&W__&T`_F(P+?_\4T#!_``B0>R`R$*P"`!P`$Y=3G5.F
XM5?_^0FW__C`M__[!_``B0>R!H#(M__Y20</\`")#[(&@THDA@0@`,"W__L'\!
XM`")![(&D0G`(`#`M__[!_``B0>R!IC(M__[#_``*,8$(`#`M__[!_``B0>R!!
XMJ#&\`'`(`#`M__[!_``B0>R!JC&\``H(`#`M__[!_``B0>R!K#&\`)((`#`M4
XM__[!_``B0>R!KD*P"``P+?_^P?P`(D'L@;(R+?_^P_P`%$/L@BC2B2&!"``P(
XM+?_^P?P`(D'L@;9"L`@`,"W__L'\`")![(&Z0C`(`#`M__[!_``B0>R!O$*P7
XM"``P+?_^P?P`(D'L@<!"<`@`,"W__L'\`!1![((H0C`(`#`M__[!_``40>R"_
XM*1&\``$(`#`M__[!_``40>R"*A&\``$(`#`M__[!_``40>R"+$)P"``P+?_^S
XMP?P`%$'L@BXQO``!"``P+?_^P?P`%$'L@C!"L`@`,"W__L'\`!1![((X0K`(T
XM`%)M__X,;0`$__YM`/Z20JR"!D'Z`"0I2((T0?H`)BE(@DA!^@`I*4B"7$'ZY
XM`"PI2()P<`!.74YU17)A<V4@06QL`$AO;&QO=R!";W@`1FEL;&5D($)O>`!0]
XM96X@1')A=P``3E4``$'L@I8I2()X.7P`"H)\0FR"?CE\`#*"@#E\``J"@CE\@
XM``&"A$'Z`$0I2(*&0>R`R"E(@HI"K(*6.7P`08*:0FR"G#E\`%6"GCE\``J"`
XMH#E\``&"HD'Z`!HI2(*D0>R!H"E(@JAP`$Y=3G5#;VQO<@!$<F%W36]D90``_
XM3E7^RAM\``'_^SM\`![_^#M\`![_]CM\`!3_]#M\`!3_\D)M_]I";?_80FW_)
XMUD)M_]1";?_20FW_T$)M_\Y(>``!2'H&JDZZ&-Q03RE`@,!*K(#`9@A"ITZZ>
XM%7!83TAX``%(>@:<3KH8O%!/*4"`Q$JL@,1F"$*G3KH54%A/2&R`!DZZ&998G
XM3RE`@K1F"$*G3KH5.%A/(&R"M!`H`#9(@#E`@K@@;(*T$"@`-TB`.4""NB!LF
XM@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00*0`Y2(`R*``*DD!3\
XM03E!@KX_/``"3KKZ\E1/3KK\Q$ZZ_HQ(;()X+RR"M$ZZ&2103T*G(&R"M"\H.
XM`#).NABZ4$\0+?_[2(`_`"!L@K0O*``R3KH8E%Q/2'H&H$AZ!>A(;(`V2'H%;
XMUTZZ"<I/[P`0*T#_RD(M_LH[?``!_^I@+#`M_^I(P.6`(&T`"B\P"`!(;?[*&
XM3KH38%!/2'H%K4AM_LI.NA-24$]2;?_J,"W_ZK!M``AMRDHM_LIG"DAM_LI.<
XMN@S@6$]*;("09P`%*"!L@K0B:`!6<``0*0`/<@'AH8*M_\HO`4ZZ%ZY83TZZ:
XM"D!";?_:(&R"M"\H`%9.NA<P6$\K0/_\9P`$*B!M__PK:``4_^X@;?_\.V@`Y
XM&/_L(&R"M#MH``[_ZB!L@K0[:``,_^@O+?_\3KH73EA/,"W_ZK!L@KAM'C`MF
XM_^JP;(*\;A0P+?_HL&R"NFT*,"W_Z+!L@KYO"#M\``'_V&`$0FW_V&<,2FW_1
XMTF<&.WP``?_6("W_[F```WH[?``!_]I@``.@(&R"M!`H`#9(@#E`@K@@;(*TD
XM$"@`-TB`.4""NB!L@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00!
XM*0`Y2(`R*``*DD!303E!@KY@``-,0FR`D&```T1P`#`M_^Q@``'`0FW_UD)M.
XM_])@``'.2FW_V&8``<9*;?_49SX[?``!_])";?_60J<@;(*T+R@`,DZZ%M)0K
XM3S`M_^A(P"\`,"W_ZDC`+P`@;(*T+R@`,DZZ%H!/[P`,8``!@DIM_]!G``#(2
XM0J<@;(*T+R@`,DZZ%I903T)M_]`@;(*T"*@````92FW_SF=Z,"W_]+!M__AL%
XM#CMM__3_YCMM__C_XF`,.VW_^/_F.VW_]/_B,"W_\K!M__9L#CMM__+_Y#MMX
XM__;_X&`,.VW_]O_D.VW_\O_@,"W_X$C`+P`P+?_B2,`O`#`M_^1(P"\`,"W_S
XMYDC`+P`@;(*T+R@`,DZZ%>I/[P`48"00+?_[2(`_`"\L@K0_+?_V/RW_^#\M$
XM__(_+?_T3KKW$D_O``Y@``"T.VW_ZO_X.VW_ZO_T.VW_Z/_V.VW_Z/_R2'@`1
XM`B!L@K0O*``R3KH5ME!/.WP``?_0(&R"M`CH````&1`M__M(@#\`+RR"M#\M,
XM__8_+?_X/RW_\C\M__1.NO:P3^\`#F!20FW_T"!L@K0(J````!D0+?_[2(`_'
XM`"\L@K0_+?_V/RW_^#\M__(_+?_T3KKV?$_O``Y@'F``_4*0O````&AG`/Y&3
XM4X!GNI"\````?V<`_BQ@XF```5X,;?___^QG``#>,"W_[,!\`!\[0/_>,"W_/
XM[.I(P'P`/SM`_]QP`#`M_]Y@``"L&VW_W?_[$"W_^TB`2,`O`"!L@K0O*``R$
XM3KH4SE!/8```F'``,"W_W&!J0J<@;(*T+R@`,DZZ%,)03S`L@KY(P"\`,"R"5
XMO$C`+P`P+(*Z2,`O`#`L@KA(P"\`(&R"M"\H`#).NA1R3^\`%&`Z0FW_U$)M%
XM_\Y@,$)M_]0[?``!_\Y@)#M\``'_U&`<8!K_AO_(_]+_WK"\````!&3NXX`PX
XM.P#L3OL``&`08`Y*@&<`_U)3@&<`_VY@\&!T2FW_T&<D$"W_^TB`/P`O+(*T[
XM/RW_]C\M__@_+?_R/RW_]$ZZ]49/[P`.0FW_T$)M_]9";?_2(&R"M`BH````#
XM&6`R8#!5@&<`_(Y=@&<`_.11@&<`_'B0O````/!G`/ZZD+P```$`9P#\PI"\J
XM``?^`&>.8,Y@`/O$2FW_VF<``+A*;?_89@``L$IM_])G3DIM_]9G)D)M_]8PD
XM+?_H2,`O`#`M_^I(P"\`(&R"M"\H`#).NA-83^\`#&`@,"W_Z$C`+P`P+?_J1
XM2,`O`"!L@K0O*``R3KH3)$_O``Q@6DIM_]!G5!`M__M(@#\`+RR"M#\M__8_U
XM+?_X/RW_\C\M__1.NO1J3^\`#CMM_^K_^#MM_^C_]A`M__M(@#\`+RR"M#\M9
XM__8_+?_X/RW_\C\M__1.NO0Z3^\`#F``^M1.N@32+RR"M$ZZ$S!83R\L@K1.$
XMNA,R6$](>``!3KH.Y%A/3EU.=6EN='5I=&EO;BYL:6)R87)Y`&=R87!H:6-S-
XM+FQI8G)A<GD`9G)E961R87<`9F0`(```3E7__DCG""`@;0`()%!X`$)M__X,-
XM$@`@;@A*$F<$4HI@\G`PL!)N)`P2`#EN'CM\``'__B!*4HH0$$B`,@3#_``*6
XMT$$X`)A\`#!@UDIM__YF!$)L@L@@;0`(((HP!$S?!!!.74YU3E4``"\$(&T`<
XM"`P0`"!O#B!M``A*$&<&4JT`"&#H>`!@&DAM``A.NO]R6$\R!$C!XX%![(+`-
XM,8`8`%)$N&T`#&W@*!].74YU3E4``$CG"#`D;0`()FT`##E\``&"R$JL@K1GG
XM<$)L@LH@;(*T(F@`,A`I`!E(@#@`,"R`DDC`+P`@;(*T+R@`,DZZ$:Q03R\MJ
XM`!`O"B!K``1.D%!/,`1(P"\`(&R"M"\H`#).NA&*4$]*;(+(9A9"IT*G,"R"Y
XMR$C`+P`O"DZZ!IA/[P`03-\,$$Y=3G5"ITAX``I(>``4+PI.N@9\3^\`$&#B[
XM3E4``#\\``$O+0`,3KK_"%Q/.6R"P("23EU.=4Y5```P+0`(2,#C@$'L@L`R\
XM,`@`LFR"N&P2,"T`"$C`XX!![(+`,:R"N`@`,"T`"$C`XX!![(+`,C`(`+)LS
XM@KQO$C`M``A(P..`0>R"P#&L@KP(`%)M``@P+0`(2,#C@$'L@L`R,`@`LFR"Q
XMNFP2,"T`"$C`XX!![(+`,:R"N@@`,"T`"$C`XX!![(+`,C`(`+)L@KYO$C`M]
XM``A(P..`0>R"P#&L@KX(`$Y=3G5.50``/SP`!"\M``Q.NOY`7$]"9TZZ_SY4R
XM3S\\``).NO\T5$\_+("2+RR"M#\L@L8_+(+$/RR"PC\L@L!.NO&"3^\`#DY=J
XM3G5.50``+P0_/``$+RT`#$ZZ_?9<3T)G3KK^]%1//SP``DZZ_NI43S`L@L"P*
XM;(+$;PXX+(+`.6R"Q(+`.42"Q#`L@L*P;(+&;PXX+(+".6R"QH+".42"QC`L`
XM@L9(P"\`,"R"Q$C`+P`P+(+"2,`O`#`L@L!(P"\`(&R"M"\H`#).N@^J3^\`.
XM%"@?3EU.=4Y5```_/``$+RT`#$ZZ_6Q<3T)G3KK^:E1//SP``DZZ_F!43S`L-
XM@L)(P"\`,"R"P$C`+P`@;(*T+R@`,DZZ#TY/[P`,,"R"QDC`+P`P+(+$2,`O^
XM`"!L@K0O*``R3KH/'$_O``Q.74YU3E4``"\L@K1.N@_"6$].74YU3E4``"\L6
XM@K1.N@^D6$].74YU3E4``$)L@)!.74YU3E4``#E\``&"RB\L@`)"IT*G+RT`%
XM"$ZZ!"A/[P`03EU.=4Y5```@;0`,#!``(&X.(&T`#$H09P92K0`,8.@O+0`,'
XM3KH#CEA/3EU.=5)A9&EC86P@17EE($UI;E)E>'@@,"XT`$Y5``!*K0`,9P9*;
XMK0`49@9P`$Y=3G5*K("B9CY.N@VP+RT`"$ZZ#8Q83TJ`9A!"IR\M``A.N@R`9
XM4$\I0("B3KH-\$JL@*)G$B!L@*)P`!`H``]R`>&A*4&`L"EM``R`J"EM`!"`D
XMM"EM`!2`N"`L@+!@H$Y5``!*K("L9A1*K(+,9PXO+(+,3KH,'EA/0JR"S$Y=S
XM3G5.50``2JR`HF=4+RR`HDZZ#:)83SE\``&`IDJL@+QG&B!L@+PA?````&0`L
XM("\L@+Q.N@V,6$]"K("\2JR`K&<.+RR`HDZZ#9183V$:8.QACB\L@*).N@Q8L
XM6$]"K("B0JR`L$Y=3G5.50``2.<,,$JL@*)F"$S?##!.74YU+RR`HDZZ#.18Y
XM3R1`2H!G``#8#"H`!P`(9BY*J@`L9PHO"B!J`"Q.D%A/+RH`*$ZZ`R)83R\*H
XM3KH#(%A/4ZR`K$ZZ_R!@``">*"H`*"!$2A!O#"!$#!``(&X$4H1@[D*J`"!"9
XMJ@`D>@!*;("F9PHE?````&0`(&!<*4J`O"9L@*A@+"\$+Q-A9%!/2D!F'B\34
XM3KH$E%A/2,#0A"\`+PLO"B!L@+A.D$_O``Q@!E"+2I-FT$J39AY*K("T9Q`OV
XM"B\J`"A.N@'H4$]Z`6`()7P````>`"!"K("\2D5F""\*3KH,8EA/8`#_&F``S
XM_PY.50``2.<`,"1M``@F;0`,2A)G*!`2L!-G'!`32(#0?``@$A)(@;!!9A)PW
XM8;`2;@P,$@!Z;@92BE*+8-00$DB`3-\,`$Y=3G5.50``2JR"S&<&<`%.74YU8
XM0J=(>@`63KH+P%!/*4""S&<$<`%@`G``8.)R97AX<WES;&EB+FQI8G)A<GD`:
XM`$Y5``!(YP`P2JR`HF<*#*P```$K@*QO"G``3-\,`$Y=3G67RV&<2D!G``"B;
XM(&R`HB\H``HO+("T+RR`HDZZ`9Q/[P`,)D!*@&<``((O+0`(3KH#8EA/2,`O2
XM`"\M``A.N@)(4$\G0``H9V(G?`$`````'"=M``P`+"=M`!``,"=M`!0`-"=M]
XM`!@`.$'Z`%8G2``*3KH*N$AZ`$].N@J46$\D0$J`9PHO"R\*3KH+"E!/3KH*B
XM_"`*9PI2K("L(`M@`/]:+RL`*$ZZ`1A83R`+9P@O"TZZ`1)83TZZ_19P`&``P
XM_SI215A8`%)%6%@`3E4``$*G0J="IT*G+RT`"$ZZ_P)/[P`43EU.=4Y5``!(F
XMYP`P)&T`""9J`#!"IR\J`"0O*@`@+PMA-$_O`!`O"TZZ"J)83TS?#`!.74YUF
XM3E4``$*G0J<O+0`,2'K_P"\M``A.NOZP3^\`%$Y=3G5.50``2.<,,"1M``@H'
XM+0`,*BT`$"9M`!1*A&8N""H``0`=9R8@"V<@3KK^/DI`9Q@O"TZZ`B983TC`>
XM+P`O"TZZ`0Y03RH`8`)Z`"5$`"`E10`D3KK\0DS?##!.74YU,CS_4F!P,CS^2
XMJF!J,CS_$&!D<I1@8#(\_S1@6C(\_FA@5#(\_W!@3C(\_WQ@2#(\_VI@0C(\A
XM_GI@/#(\_G1@-C(\_FY@,#(\_F)@*C(\_H!@)#(\_TQ@'G*(8!HR//\68!1R0
XMFF`0,CS_(F`*,CS_'&`$,CS_6$SO`P``!"`O``PO#BQY```"RDZV$``L7TYUB
XM,CS_=F#@,CS_.F#:,CS^C,CS^GF#.,CS^I&#(,CS_1F#",CS_0&"\,CS^J
XM1&"V,CS_!&"P,CS^_F"J,CS^^&"D,CS^\F">,CS^[&"8,CS^X&"2,CS^AF",_
XM,CS^F&"&<H)@(#(\_RA@&C(\_V1@%#(\_E!@#G*.8`HR//\N8`0R//[F(&\`F
XM!"`O``A@`/]B,CS^R&`0,CS^/F`*,CS^VF`$,CS^."`O``1@`/]$,GS^SF`0"
XM,GS^PF`*,GS_7F`$,GS^DB!O``1,[P`#``@O#BQY```"RDZVD``L7TYU3.\#L
XM`P`$P8C#B2\.+'D```+*3J[^2BQ?3G5,[P,#``3!B,.)+PXL>0```LI.KOZ\:
XM+%].=4SO`P,`!,&(PXDO#BQY```"RDZN_K8L7TYU3.\#`P`$P8C#B2\.+'D`0
XM``+*3J[^L"Q?3G4@;P`$,CS^U&$`_J`B;P`((H%.=2`O``1RH&$`_HXB;P`(`
XM(HA.=2!O``0@"$H89OR1P"`(4X!.=6%P0^R`HD7L@**UR68.,CP`GFL(=``BW
XMPE')__PI3X+0+'@`!"E.@M1(YX"`""X`!`$I9Q!+^@`(3J[_XF`&0J?S7TYSL
XM0_H`($ZN_F@I0(+89@PN/``#@`=.KO^48`1.N@`:4$].=61O<RYL:6)R87)Y(
XM`$GY``!__DYU3E4``"\*2'D``0``,"R`F,'\``8O`$ZZ!LHI0(+<4$]F%$*GG
XM2'D``0``3KH%H%!/+FR"T$YU(&R"W$)H``0@;(+<,7P``0`0(&R"W#%\``$`5
XM"B!L@M`@+(+0D*@`!%"`*4""X"!L@N`@O$U!3EA"ITZZ!HXD0$JJ`*Q83V<N@
XM+RT`#"\M``@O"DZZ`*XY?``!@N0@;(+<`&B````$(&R"W`!H@```"D_O``Q@S
XM0DAJ`%Q.N@;^2&H`7$ZZ!H(I0(+F(&R"YDJH`"103V<0(&R"YB)H`"0O$4ZZ5
XM!+I83R\L@N8O"DZZ`F@I;(+F@NI03TZZ!*X@;(+<((!.N@2Z(&R"W"%```9G=
XM%DAX`^U(>@`J3KH$EB!L@MPA0``,4$\O+(+J/RR"[DZZ[1!"9TZZ`M103R1?:
XM3EU.=2H`3E4``$CG##`D;0`0(&T`"$JH`*QG&"!M``@@*`"LY8`H`"!$("@`[
XM$.6`)D!@!"9L@)H0$TB`2,#0K0`,5(`Y0(+P0J<P+(+P2,`O`$ZZ!5PI0(+R:
XM4$]F"$S?##!.74YU$!-(@#H`/P4@2U*(+P@O+(+R3KH!?C`%2,`@0-'L@O)#N
XM^@%$$-EF_#\M``XO"B\L@O).N@$Z(&R"\D(P4``Y?``!@NXP!4C`T*R"\B9`*
XM4HLD2T_O`!00$TB`.@"P?``@9QBZ?``)9Q*Z?``,9PRZ?``-9P:Z?``*9@12G
XMBV#8#!,`(&UZ#!,`(F8N4HL@2U*+$!!(@#H`9QX@2E**$(6Z?``B9A`,$P`BT
XM9@12BV`&0BK__V`"8-9@."!+4HL0$$B`.@!G)KI\`"!G(+I\``EG&KI\``QG$
XM%+I\``UG#KI\``IG""!*4HH0A6#.($I2BD(02D5F`E.+4FR"[F``_UI"$D*G5
XM,"R"[E)`2,#E@"\`3KH$.BE`@NI03V8(0FR"[F``_MAZ`"9L@O)@)#`%2,#E<
XM@"!L@NHABP@`($L@"$H89OR1P%.(,`A20$C`U\!21;IL@NYMUC`%2,#E@"!LV
XM@NI"L`@`8`#^E"``,#Q__V`$,"\`#"!O``1*&&;\4T@B;P`(4T`0V5?(__QGY
XM`D(0("\`!$YU3.\#```$(`@R+P`,8`(0V5?)__QG!E)!8`)"&%')__Q.=4Y5>
XM``!(YPXP)&T`"$*G2'H`CDZZ`_XI0(+V4$]F"$S?#'!.74YU(&T`#")H`"0O!
XM*0`$3KH$K"@`6$]G4DAZ`&T@1"\H`#9.N@1^)D!*@%!/9S1(>`/M+PM.N@(`U
XM+`!03V<D(`;E@"H`($4E:``(`*0E1@"<2'@#[4AZ`#A.N@'<)4``H%!/+P1.D
XMN@1*6$\O+(+V3KH"!$*L@O983V"`:6-O;BYL:6)R87)Y`%=)3D1/5P`J`$Y5N
XM``!*K(+Z9P8@;(+Z3I`_+0`(3KH`"%1/3EU.=4Y5__PO!#`M``A(P"M`__Q*(
XMK(+<9RAX`&`*/P1.N@#^5$]21+AL@)AM\#`L@)C!_``&+P`O+(+<3KH"Q%!/B
XM2JR"_F<&(&R"_DZ02JR`GF<*+RR`GDZZ`4A83TJL@P)G""!L@P(@K(,&2JR#,
XM"F<*+RR#"DZZ`5983TJL@PYG"B\L@PY.N@%&6$]*K(,29PHO+(,23KH!-EA/5
XM2JR#%F<*+RR#%DZZ`2983RQX``0(+@`$`2EG%"\-2_H`"DZN_^(J7V`&0J?S$
XM7TYS2JR"YF8P2JR"\F<H,"R"\$C`+P`O+(+R3KH"'#`L@NY20$C`Y8`O`"\L<
XM@NI.N@((3^\`$&`.3KH!\B\L@N9.N@)N6$\@+?_\+FR"T$YU*!].74YU3E4`F
XM`$CG#B`X+0`(,`3!_``&)$#5[(+<2D1M"KAL@)AL!$J29A`Y?``"@QIP_TS?U
XM!'!.74YU""H`!P`$9@@O$DZZ``I83T*2<`!@XB(O``0L;(+83N[_W"(O``0LL
XM;(+83N[_@BQL@MA.[O_*3.\`!@`$+&R"V$[N_^(L;(+83N[_Q$[Z``(B+P`$>
XM+&R"V$[N_Z9(YP$$3.\@@``,+&R"U$ZN_Y1,WR"`3G5.^@`"(F\`!"QL@M1.4
XM[OYB3E4``$CG""!(>/__3KH`T"@`L+S_____6$]F"G``3-\$$$Y=3G5(>0`!D
XM``%(>``B3KH`N"1`2H!03V8,+P1.N@#X<`!83V#6)6T`"``*%6T`#P`)%7P`Q
XM!``(0BH`#A5$``]"ITZZ`*(E0``02JT`"%A/9PHO"DZZ`%I83V`*2&H`%$ZZ'
XM`-!83R`*8)).50``+PHD;0`(2JH`"F<(+PI.N@#P6$\5?`#_``@E?/____\`"
XM%'``$"H`#R\`3KH`?$AX`"(O"DZZ`%Y/[P`,)%].74YU(F\`!"QL@M1.[OZ>7
XM("\`!"QL@M1.[OZV3OH``DSO``,`!"QL@M1.[O\Z(F\`!"QL@M1.[OYZ3OH`?
XM`B)O``0L;(+43N[^VD[Z``(L;(+43N[_?$[Z``(B;P`$("\`""QL@M1.[O\N5
XM("\`!"QL@M1.[OZP3OH``B!O``0L;(+43N[^C"!O``0@B%B00J@`!"%(``A.,
XM=4[Z``(L;(+4(F\`!"`O``A.[OW8+&R"U$[N_W9,[P,```0L;(+43N[^DB)O6
XM``0L;(+43N[^F$[Z``(B;P`$+&R"U$[N_H8@+P`$+&R"U$[N_L).^@`"(&\`G
XM!"QL@M1.[OZ`(F\`!$SO``,`""QL@,1.[O\*(F\`!$SO``,`""QL@,1.[O\0J
XM(F\`!$SO``\`""QL@,1.[O[.(F\`!"`O``@L;(#$3N[^JB)O``0@+P`(+&R`[
XMQ$[N_IY,[P,```0L;(+V3N[_H"!O``0L;(+V3N[_IB!O``0L;(+V3N[_LB!OL
XM``0L;(#`3N[_RB!O``0L;(#`3N[_N"!O``0L;(#`3N[_-$SO`P``!"QL@,!.!
XM[O[X(&\`!"QL@,!.[O[.(&\`!"QL@,!.[O[(```#[`````<````!```6A```5
XM%FH``!90```6-@``%AP``!4^```7/`````````/R```#Z@```"@````$``H`B
XM"@)8`+0``0`(`QH``!(/```````````````F````````````9``C_____P`!)
XM````.@``#>@```!````.L````$0```[X````20``#X0```!.```/[````%8`R
XM``_^````70``$!````!B```0'````&H``!`^`````````````@`$``@`$``@.
XM``$``0``$&@`%``````````````#[````!4``````````````!X````T````D
XM.````#P```!`````1````$@```!,````4````%0```!8````7````&````!DH
XL````:````&P```!P````=````'@```"2`````````_(```/K`````0```_)D_
X``
Xend
Xsize 8504
SHAR_EOF
echo "extracting aspline.fd"
sed 's/^X//' << \SHAR_EOF > aspline.fd
X/* given four control points (that's eight arguments) this draws a bspline */
X
Xparse arg x1 y1 x2 y2 x3 y3 x4 y4
X
Xlength = max(abs(x1-x4), abs(y1-74))
X
Xif (length = 0) then exit
X
Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length
Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length
X
X/* if they are colinear, simply draw a line. */
X
Xif (d1 < 0.5) & (d2 < 0.5) then do
X
X address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4)
X
X/* otherwise invoke recursively on substrings */
X
Xend
Xelse do
X
X x12 = (x1 + x2) / 2
X y12 = (y1 + y2) / 2
X x23 = (x2 + x3) / 2
X y23 = (y2 + y3) / 2
X x34 = (x3 + x4) / 2
X y34 = (y3 + y4) / 2
X x123 = (x12 + x23) / 2
X y123 = (y12 + y23) / 2
X x234 = (x23 + x34) / 2
X y234 = (y23 + y34) / 2
X x1234 = (x123 + x234) / 2
X y1234 = (y123 + y234) / 2
X
X address 'freedraw' 'Spawn aspline ' x1 y1 x12 y12 x123 y123 x1234 y1234
X address 'freedraw' 'Spawn aspline ' x1234 y1234 x234 y234 x34 y34 x4 y4
X
Xend
SHAR_EOF
echo "extracting bspline.fd"
sed 's/^X//' << \SHAR_EOF > bspline.fd
X/* given four control points (that's eight arguments) this draws a bspline */
X
Xparse arg x1 y1 x2 y2 x3 y3 x4 y4
X
Xlength = max(abs(x1-x4), abs(y1-74))
X
Xif (length = 0) then exit
X
Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length
Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length
X
X/* if they are colinear, simply draw a line. */
X
Xif (d1 < 0.5) & (d2 < 0.5) then do
X
X address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4)
X
X/* otherwise invoke recursively on substrings */
X
Xend
Xelse do
X
X x12 = (x1 + x2) / 2
X y12 = (y1 + y2) / 2
X x23 = (x2 + x3) / 2
X y23 = (y2 + y3) / 2
X x34 = (x3 + x4) / 2
X y34 = (y3 + y4) / 2
X x123 = (x12 + x23) / 2
X y123 = (y12 + y23) / 2
X x234 = (x23 + x34) / 2
X y234 = (y23 + y34) / 2
X x1234 = (x123 + x234) / 2
X y1234 = (y123 + y234) / 2
X
X address 'freedraw' 'bspline ' x1 y1 x12 y12 x123 y123 x1234 y1234
X address 'freedraw' 'bspline ' x1234 y1234 x234 y234 x34 y34 x4 y4
X
Xend
SHAR_EOF
echo "extracting sample.fd"
sed 's/^X//' << \SHAR_EOF > sample.fd
X/* draw something with freedraw */
X
Xaddress 'freedraw'
X
Xcolor = 0
X
Xdo i = 20 to 400
X 'Color ' color
X color = color + 1
X if color > 3 then color = 0
X 'Line ' 20 i i 400
Xend i
SHAR_EOF
echo "extracting saspline.fd"
sed 's/^X//' << \SHAR_EOF > saspline.fd
X/* invokes aspline with some sample values */
X'aspline 314 159 10 9 456 44 536 141'
Xexit
SHAR_EOF
echo "extracting sbspline.fd"
sed 's/^X//' << \SHAR_EOF > sbspline.fd
X/* invokes aspline with some sample values */
X'bspline 314 159 10 9 456 44 536 141'
Xexit
SHAR_EOF
echo "extracting version.fd"
sed 's/^X//' << \SHAR_EOF > version.fd
X/* gets the version from freedraw */
X
Xoptions results
X
Xaddress 'freedraw' 'Version'
X
Xsay result
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit